// Free Disassembler and Assembler -- Disassembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#pragma hdrstop
#pragma warning(disable:4996)

#include "disasm.h"

////////////////////////////////////////////////////////////////////////////////
//////////////////////////// DISASSEMBLER FUNCTIONS ////////////////////////////

// Work variables of disassembler
static ulong     datasize;             // Size of data (1,2,4 bytes)
static ulong     addrsize;             // Size of address (2 or 4 bytes)
static int       segprefix;            // Segment override prefix or SEG_UNDEF
static int       hasrm;                // Command has ModR/M byte
static int       hassib;               // Command has SIB byte
static int       dispsize;             // Size of displacement (if any)
static int       immsize;              // Size of immediate data (if any)
static int       softerror;            // Noncritical disassembler error
static int       ndump;                // Current length of command dump
static int       nresult;              // Current length of disassembly
static int       addcomment;           // Comment value of operand

// Copy of input parameters of function Disasm()
static char      *cmd;                 // Pointer to binary data
static char      *pfixup;              // Pointer to possible fixups or NULL
static ulong     size;                 // Remaining size of the command buffer
static t_disasm  *da;                  // Pointer to disassembly results
static int       mode;                 // Disassembly mode (DISASM_xxx)

// Disassemble name of 1, 2 or 4-byte general-purpose integer register and, if
// requested and available, dump its contents. Parameter type changes decoding
// of contents for some operand types.
static void DecodeRG( int index, int datasize, int type )
{
    int sizeindex;
    char name[9];

    if ( mode < DISASM_DATA ) return;    // No need to decode

    index &= 0x07;

    if ( datasize == 1 )
        sizeindex = 0;
    else if ( datasize == 2 )
        sizeindex = 1;
    else if ( datasize == 4 )
        sizeindex = 2;
    else
    {
        da->error = DAE_INTERN;
        return;
    };

    if ( mode >= DISASM_FILE )
    {
        strcpy( name, regname[sizeindex][index] );

        if ( lowercase ) strlwr( name );

        if ( type < PSEUDOOP )             // Not a pseudooperand
            nresult += sprintf( da->result + nresult, "%s", name );

        ;
    };
};

// Disassemble name of 80-bit floating-point register and, if available, dump
// its contents.
static void DecodeST( int index, int pseudoop )
{
    int i;
    char s[32];

    if ( mode < DISASM_FILE ) return;    // No need to decode

    index &= 0x07;
    i = sprintf( s, "%s(%i)", ( lowercase ? "st" : "ST" ), index );

    if ( pseudoop == 0 )
    {
        strcpy( da->result + nresult, s );
        nresult += i;
    };
};

// Disassemble name of 64-bit MMX register.
static void DecodeMX( int index )
{
    char *pr;

    if ( mode < DISASM_FILE ) return;    // No need to decode

    index &= 0x07;
    pr = da->result + nresult;
    nresult += sprintf( pr, "%s%i", ( lowercase ? "mm" : "MM" ), index );
};

// Disassemble name of 64-bit 3DNow! register and, if available, dump its
// contents.
static void DecodeNR( int index )
{
    char *pr;

    if ( mode < DISASM_FILE ) return;    // No need to decode

    index &= 0x07;
    pr = da->result + nresult;
    nresult += sprintf( pr, "%s%i", ( lowercase ? "mm" : "MM" ), index );
};

// Service function, adds valid memory adress in MASM or Ideal format to
// disassembled string. Parameters: defseg - default segment for given
// register combination, descr - fully decoded register part of address,
// offset - constant part of address, dsize - data size in bytes. If global
// flag 'symbolic' is set, function also tries to decode offset as name of
// some label.
static void Memadr( int defseg, const char *descr, long offset, int dsize )
{
    int i, n, seg;
    char *pr;
    char s[TEXTLEN];

    if ( mode < DISASM_FILE || descr == NULL )
        return;                            // No need or possibility to decode

    pr = da->result + nresult;
    n = 0;

    if ( segprefix != SEG_UNDEF ) seg = segprefix;
    else seg = defseg;

    if ( ideal != 0 ) pr[n++] = '[';

    // In some cases Disassembler may omit size of memory operand. Namely, flag
    // showmemsize must be 0, type bit C_EXPL must be 0 (this bit namely means
    // that explicit operand size is necessary) and type of command must not be
    // C_MMX or C_NOW (because bit C_EXPL has in these cases different meaning).
    // Otherwise, exact size must be supplied.
    if ( showmemsize != 0 || ( da->cmdtype & C_TYPEMASK ) == C_MMX ||
            ( da->cmdtype & C_TYPEMASK ) == C_NOW || ( da->cmdtype & C_EXPL ) != 0
       )
    {
        if ( dsize < sizeof( sizename ) / sizeof( sizename[0] ) )
            n += sprintf( pr + n, "%s %s", sizename[dsize], ( ideal == 0 ? "PTR " : "" ) );
        else
            n += sprintf( pr + n, "(%i-BYTE) %s", dsize, ( ideal == 0 ? "PTR " : "" ) );

        ;
    };

    if ( ( putdefseg != 0 || seg != defseg ) && seg != SEG_UNDEF )
        n += sprintf( pr + n, "%s:", segname[seg] );

    if ( ideal == 0 ) pr[n++] = '[';

    n += sprintf( pr + n, "%s", descr );

    if ( lowercase ) strlwr( pr );

    if ( offset == 0L )
    {
        if ( *descr == '\0' ) pr[n++] = '0';
    }
    else
    {
        if ( symbolic && mode >= DISASM_CODE )
            i = Decodeaddress( offset, s, TEXTLEN - n - 24, NULL );
        else i = 0;

        if ( i > 0 )                       // Offset decoded in symbolic form
        {
            if ( *descr != '\0' ) pr[n++] = '+';

            strcpy( pr + n, s );
            n += i;
        }
        else if ( offset < 0 && offset > -16384 && *descr != '\0' )
            n += sprintf( pr + n, "-%lX", -offset );
        else
        {
            if ( *descr != '\0' ) pr[n++] = '+';

            n += sprintf( pr + n, "%lX", offset );
        };
    };

    pr[n++] = ']';

    pr[n] = '\0';

    nresult += n;
};

// Disassemble memory/register from the ModRM/SIB bytes and, if available, dump
// address and contents of memory.
static void DecodeMR( int type )
{
    int j, memonly, inmemory, seg;
    int c, sib;
    ulong dsize, regsize, addr;
    char s[TEXTLEN];

    if ( size < 2 )
    {
        da->error = DAE_CROSS;
        return;
    };    // ModR/M byte outside the memory block

    hasrm = 1;

    dsize = regsize = datasize;          // Default size of addressed reg/memory

    memonly = 0;                         // Register in ModM field is allowed

    // Size and kind of addressed memory or register in ModM has no influence on
    // the command size, and exact calculations are omitted if only command size
    // is requested. If register is used, optype will be incorrect and we need
    // to correct it later.
    c = cmd[1] & 0xC7;                   // Leave only Mod and M fields

    if ( mode >= DISASM_DATA )
    {
        if ( ( c & 0xC0 ) == 0xC0 )        // Register operand
            inmemory = 0;
        else                               // Memory operand
            inmemory = 1;

        switch ( type )
        {
        case MRG:                        // Memory/register in ModRM byte
            if ( inmemory )
            {
                if ( datasize == 1 ) da->memtype = DEC_BYTE;
                else if ( datasize == 2 ) da->memtype = DEC_WORD;
                else da->memtype = DEC_DWORD;
            };

            break;

        case MRJ:                        // Memory/reg in ModRM as JUMP target
            if ( datasize != 2 && inmemory )
                da->memtype = DEC_DWORD;

            if ( mode >= DISASM_FILE && shownear != 0 )
                nresult += sprintf( da->result + nresult, "%s ", ( lowercase ? "near" : "NEAR" ) );

            break;

        case MR1:                        // 1-byte memory/register in ModRM byte
            dsize = regsize = 1;

            if ( inmemory ) da->memtype = DEC_BYTE;

            break;

        case MR2:                        // 2-byte memory/register in ModRM byte
            dsize = regsize = 2;

            if ( inmemory ) da->memtype = DEC_WORD;

            break;

        case MR4:                        // 4-byte memory/register in ModRM byte
        case RR4:                        // 4-byte memory/register (register only)
            dsize = regsize = 4;

            if ( inmemory ) da->memtype = DEC_DWORD;

            break;

        case MR8:                        // 8-byte memory/MMX register in ModRM
        case RR8:                        // 8-byte MMX register only in ModRM
            dsize = 8;

            if ( inmemory ) da->memtype = DEC_QWORD;

            break;

        case MRD:                        // 8-byte memory/3DNow! register in ModRM
        case RRD:                        // 8-byte memory/3DNow! (register only)
            dsize = 8;

            if ( inmemory ) da->memtype = DEC_3DNOW;

            break;

        case MMA:                        // Memory address in ModRM byte for LEA
            memonly = 1;
            break;

        case MML:                        // Memory in ModRM byte (for LES)
            dsize = datasize + 2;
            memonly = 1;

            if ( datasize == 4 && inmemory )
                da->memtype = DEC_FWORD;

            da->warnings |= DAW_SEGMENT;
            break;

        case MMS:                        // Memory in ModRM byte (as SEG:OFFS)
            dsize = datasize + 2;
            memonly = 1;

            if ( datasize == 4 && inmemory )
                da->memtype = DEC_FWORD;

            if ( mode >= DISASM_FILE )
                nresult += sprintf( da->result + nresult, "%s ", ( lowercase ? "far" : "FAR" ) );

            break;

        case MM6:                        // Memory in ModRM (6-byte descriptor)
            dsize = 6;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_FWORD;

            break;

        case MMB:                        // Two adjacent memory locations (BOUND)
            dsize = ( ideal ? datasize : datasize * 2 );
            memonly = 1;
            break;

        case MD2:                        // Memory in ModRM byte (16-bit integer)
        case MB2:                        // Memory in ModRM byte (16-bit binary)
            dsize = 2;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_WORD;

            break;

        case MD4:                        // Memory in ModRM byte (32-bit integer)
            dsize = 4;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_DWORD;

            break;

        case MD8:                        // Memory in ModRM byte (64-bit integer)
            dsize = 8;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_QWORD;

            break;

        case MDA:                        // Memory in ModRM byte (80-bit BCD)
            dsize = 10;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_TBYTE;

            break;

        case MF4:                        // Memory in ModRM byte (32-bit float)
            dsize = 4;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_FLOAT4;

            break;

        case MF8:                        // Memory in ModRM byte (64-bit float)
            dsize = 8;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_FLOAT8;

            break;

        case MFA:                        // Memory in ModRM byte (80-bit float)
            dsize = 10;
            memonly = 1;

            if ( inmemory ) da->memtype = DEC_FLOAT10;

            break;

        case MFE:                        // Memory in ModRM byte (FPU environment)
            dsize = 28;
            memonly = 1;
            break;

        case MFS:                        // Memory in ModRM byte (FPU state)
            dsize = 108;
            memonly = 1;
            break;

        case MFX:                        // Memory in ModRM byte (ext. FPU state)
            dsize = 512;
            memonly = 1;
            break;

        default:                         // Operand is not in ModM!
            da->error = DAE_INTERN;
            break;
        };
    };

    addr = 0;

    // There are many possibilities to decode ModM/SIB address. The first
    // possibility is register in ModM - general-purpose, MMX or 3DNow!
    if ( ( c & 0xC0 ) == 0xC0 )          // Decode register operand
    {
        if ( type == MR8 || type == RR8 )
            DecodeMX( c );                   // MMX register
        else if ( type == MRD || type == RRD )
            DecodeNR( c );                   // 3DNow! register
        else
            DecodeRG( c, regsize, type );    // General-purpose register

        if ( memonly != 0 )
            softerror = DAE_MEMORY;          // Register where only memory allowed

        return;
    };

    // Next possibility: 16-bit addressing mode, very seldom in 32-bit flat model
    // but still supported by processor. SIB byte is never used here.
    if ( addrsize == 2 )
    {
        if ( c == 0x06 )                   // Special case of immediate address
        {
            dispsize = 2;

            if ( size < 4 )
                da->error = DAE_CROSS;         // Disp16 outside the memory block
            else if ( mode >= DISASM_DATA )
            {
                da->adrconst = addr = *( ushort * )( cmd + 2 );

                if ( addr == 0 ) da->zeroconst = 1;

                seg = SEG_DS;
                Memadr( seg, "", addr, dsize );
            };
        }
        else
        {
            da->indexed = 1;

            if ( ( c & 0xC0 ) == 0x40 )      // 8-bit signed displacement
            {
                if ( size < 3 ) da->error = DAE_CROSS;
                else addr = ( signed char )cmd[2] & 0xFFFF;

                dispsize = 1;
            }
            else if ( ( c & 0xC0 ) == 0x80 ) // 16-bit unsigned displacement
            {
                if ( size < 4 ) da->error = DAE_CROSS;
                else addr = *( ushort * )( cmd + 2 );

                dispsize = 2;
            };

            if ( mode >= DISASM_DATA && da->error == DAE_NOERR )
            {
                da->adrconst = addr;

                if ( addr == 0 ) da->zeroconst = 1;

                seg = addr16[c & 0x07].defseg;
                Memadr( seg, addr16[c & 0x07].descr, addr, dsize );
            };
        };
    }
    // Next possibility: immediate 32-bit address.
    else if ( c == 0x05 )                // Special case of immediate address
    {
        dispsize = 4;

        if ( size < 6 )
            da->error = DAE_CROSS;           // Disp32 outside the memory block
        else if ( mode >= DISASM_DATA )
        {
            da->adrconst = addr = *( ulong * )( cmd + 2 );

            if ( pfixup == NULL ) pfixup = cmd + 2;

            da->fixupsize += 4;

            if ( addr == 0 ) da->zeroconst = 1;

            seg = SEG_DS;
            Memadr( seg, "", addr, dsize );
        };
    }
    // Next possibility: 32-bit address with SIB byte.
    else if ( ( c & 0x07 ) == 0x04 )     // SIB addresation
    {
        sib = cmd[2];
        hassib = 1;
        *s = '\0';

        if ( c == 0x04 && ( sib & 0x07 ) == 0x05 )
        {
            dispsize = 4;                    // Immediate address without base

            if ( size < 7 )
                da->error = DAE_CROSS;         // Disp32 outside the memory block
            else
            {
                da->adrconst = addr = *( ulong * )( cmd + 3 );

                if ( pfixup == NULL ) pfixup = cmd + 3;

                da->fixupsize += 4;

                if ( addr == 0 ) da->zeroconst = 1;

                if ( ( sib & 0x38 ) != 0x20 )  // Index register present
                {
                    da->indexed = 1;

                    if ( type == MRJ ) da->jmptable = addr;
                };

                seg = SEG_DS;
            };
        }
        else                               // Base and, eventually, displacement
        {
            if ( ( c & 0xC0 ) == 0x40 )      // 8-bit displacement
            {
                dispsize = 1;

                if ( size < 4 ) da->error = DAE_CROSS;
                else
                {
                    da->adrconst = addr = ( signed char )cmd[3];

                    if ( addr == 0 ) da->zeroconst = 1;
                };
            }
            else if ( ( c & 0xC0 ) == 0x80 ) // 32-bit displacement
            {
                dispsize = 4;

                if ( size < 7 )
                    da->error = DAE_CROSS;       // Disp32 outside the memory block
                else
                {
                    da->adrconst = addr = *( ulong * )( cmd + 3 );

                    if ( pfixup == NULL ) pfixup = cmd + 3;

                    da->fixupsize += 4;

                    if ( addr == 0 ) da->zeroconst = 1;

                    // Most compilers use address of type [index*4+displacement] to
                    // address jump table (switch). But, for completeness, I allow all
                    // cases which include index with scale 1 or 4, base or both.
                    if ( type == MRJ ) da->jmptable = addr;
                };
            };

            da->indexed = 1;

            j = sib & 0x07;

            if ( mode >= DISASM_FILE )
            {
                strcpy( s, regname[2][j] );
                seg = addr32[j].defseg;
            };
        };

        if ( ( sib & 0x38 ) != 0x20 )      // Scaled index present
        {
            if ( ( sib & 0xC0 ) == 0x40 ) da->indexed = 2;
            else if ( ( sib & 0xC0 ) == 0x80 ) da->indexed = 4;
            else if ( ( sib & 0xC0 ) == 0xC0 ) da->indexed = 8;
            else da->indexed = 1;
        };

        if ( mode >= DISASM_FILE && da->error == DAE_NOERR )
        {
            if ( ( sib & 0x38 ) != 0x20 )    // Scaled index present
            {
                if ( *s != '\0' ) strcat( s, "+" );

                strcat( s, addr32[( sib >> 3 ) & 0x07].descr );

                if ( ( sib & 0xC0 ) == 0x40 )
                {
                    da->jmptable = 0;            // Hardly a switch!
                    strcat( s, "*2" );
                }
                else if ( ( sib & 0xC0 ) == 0x80 )
                    strcat( s, "*4" );
                else if ( ( sib & 0xC0 ) == 0xC0 )
                {
                    da->jmptable = 0;            // Hardly a switch!
                    strcat( s, "*8" );
                };
            };

            Memadr( seg, s, addr, dsize );
        };
    }
    // Last possibility: 32-bit address without SIB byte.
    else                                 // No SIB
    {
        if ( ( c & 0xC0 ) == 0x40 )
        {
            dispsize = 1;

            if ( size < 3 ) da->error = DAE_CROSS; // Disp8 outside the memory block
            else
            {
                da->adrconst = addr = ( signed char )cmd[2];

                if ( addr == 0 ) da->zeroconst = 1;
            };
        }
        else if ( ( c & 0xC0 ) == 0x80 )
        {
            dispsize = 4;

            if ( size < 6 )
                da->error = DAE_CROSS;         // Disp32 outside the memory block
            else
            {
                da->adrconst = addr = *( ulong * )( cmd + 2 );

                if ( pfixup == NULL ) pfixup = cmd + 2;

                da->fixupsize += 4;

                if ( addr == 0 ) da->zeroconst = 1;

                if ( type == MRJ ) da->jmptable = addr;
            };
        };

        da->indexed = 1;

        if ( mode >= DISASM_FILE && da->error == DAE_NOERR )
        {
            seg = addr32[c & 0x07].defseg;
            Memadr( seg, addr32[c & 0x07].descr, addr, dsize );
        };
    };
};

// Disassemble implicit source of string operations and, if available, dump
// address and contents.
static void DecodeSO( void )
{
    if ( mode < DISASM_FILE ) return;    // No need to decode

    if ( datasize == 1 ) da->memtype = DEC_BYTE;
    else if ( datasize == 2 ) da->memtype = DEC_WORD;
    else if ( datasize == 4 ) da->memtype = DEC_DWORD;

    da->indexed = 1;
    Memadr( SEG_DS, regname[addrsize == 2 ? 1 : 2][REG_ESI], 0L, datasize );
};

// Disassemble implicit destination of string operations and, if available,
// dump address and contents. Destination always uses segment ES, and this
// setting cannot be overridden.
static void DecodeDE( void )
{
    int seg;

    if ( mode < DISASM_FILE ) return;    // No need to decode

    if ( datasize == 1 ) da->memtype = DEC_BYTE;
    else if ( datasize == 2 ) da->memtype = DEC_WORD;
    else if ( datasize == 4 ) da->memtype = DEC_DWORD;

    da->indexed = 1;
    seg = segprefix;
    segprefix = SEG_ES;   // Fake Memadr by changing segment prefix
    Memadr( SEG_DS, regname[addrsize == 2 ? 1 : 2][REG_EDI], 0L, datasize );
    segprefix = seg;                     // Restore segment prefix
};

// Decode XLAT operand and, if available, dump address and contents.
static void DecodeXL( void )
{
    if ( mode < DISASM_FILE ) return;    // No need to decode

    da->memtype = DEC_BYTE;
    da->indexed = 1;
    Memadr( SEG_DS, ( addrsize == 2 ? "BX+AL" : "EBX+AL" ), 0L, 1 );
};

// Decode immediate operand of size constsize. If sxt is non-zero, byte operand
// should be sign-extended to sxt bytes. If type of immediate constant assumes
// this, small negative operands may be displayed as signed negative numbers.
// Note that in most cases immediate operands are not shown in comment window.
static void DecodeIM( int constsize, int sxt, int type )
{
    int i;
    signed long data;
    ulong l;
    char name[TEXTLEN], comment[TEXTLEN];
    immsize += constsize;                  // Allows several immediate operands

    if ( mode < DISASM_DATA ) return;

    l = 1 + hasrm + hassib + dispsize + ( immsize - constsize );
    data = 0;

    if ( size < l + constsize )
        da->error = DAE_CROSS;
    else if ( constsize == 1 )
    {
        if ( sxt == 0 ) data = ( uchar )cmd[l];
        else data = ( signed char )cmd[l];

        if ( type == IMS && ( ( data & 0xE0 ) != 0 || data == 0 ) )
        {
            da->warnings |= DAW_SHIFT;
            da->cmdtype |= C_RARE;
        };
    }
    else if ( constsize == 2 )
    {
        if ( sxt == 0 ) data = *( ushort * )( cmd + l );
        else data = *( short * )( cmd + l );
    }
    else
    {
        data = *( long * )( cmd + l );

        if ( pfixup == NULL ) pfixup = cmd + l;

        da->fixupsize += 4;
    };

    if ( sxt == 2 ) data &= 0x0000FFFF;

    if ( data == 0 && da->error == 0 ) da->zeroconst = 1;

    // Command ENTER, as an exception from Intel's rules, has two immediate
    // constants. As the second constant is rarely used, I exclude it from
    // search if the first constant is non-zero (which is usually the case).
    if ( da->immconst == 0 )
        da->immconst = data;

    if ( mode >= DISASM_FILE && da->error == DAE_NOERR )
    {
        if ( mode >= DISASM_CODE && type != IMU )
            i = Decodeaddress( data, name, TEXTLEN - nresult - 24, comment );
        else
        {
            i = 0;
            comment[0] = '\0';
        };

        if ( i != 0 && symbolic != 0 )
        {
            strcpy( da->result + nresult, name );
            nresult += i;
        }
        else if ( type == IMU || type == IMS || type == IM2 || data >= 0 || data < NEGLIMIT )
            nresult += sprintf( da->result + nresult, "%lX", data );
        else
            nresult += sprintf( da->result + nresult, "-%lX", -data );

        if ( addcomment && comment[0] != '\0' ) strcpy( da->comment, comment );
    };
};

// Decode VxD service name (always 4-byte).
static void DecodeVX( void )
{
    ulong l, data;
    immsize += 4;                        // Allows several immediate operands

    if ( mode < DISASM_DATA ) return;

    l = 1 + hasrm + hassib + dispsize + ( immsize - 4 );

    if ( size < l + 4 )
    {
        da->error = DAE_CROSS;
        return;
    };

    data = *( long * )( cmd + l );

    if ( data == 0 && da->error == 0 ) da->zeroconst = 1;

    if ( da->immconst == 0 )
        da->immconst = data;

    if ( mode >= DISASM_FILE && da->error == DAE_NOERR )
    {
        if ( ( data & 0x00008000 ) != 0 && memicmp( "VxDCall", da->result, 7 ) == 0 )
            memcpy( da->result, lowercase ? "vxdjump" : "VxDJump", 7 );

        nresult += sprintf( da->result + nresult, "%lX", data );
    };
};

// Decode implicit constant 1 (used in shift commands). This operand is so
// insignificant that it is never shown in comment window.
static void DecodeC1( void )
{
    if ( mode < DISASM_DATA ) return;

    da->immconst = 1;

    if ( mode >= DISASM_FILE ) nresult += sprintf( da->result + nresult, "1" );
};

// Decode immediate absolute data address. This operand is used in 8080-
// compatible commands which allow to move data from memory to accumulator and
// back. Note that bytes ModRM and SIB never appear in commands with IA operand.
static void DecodeIA( void )
{
    ulong addr;

    if ( size < 1 + addrsize )
    {
        da->error = DAE_CROSS;
        return;
    };

    dispsize = addrsize;

    if ( mode < DISASM_DATA ) return;

    if ( datasize == 1 ) da->memtype = DEC_BYTE;
    else if ( datasize == 2 ) da->memtype = DEC_WORD;
    else if ( datasize == 4 ) da->memtype = DEC_DWORD;

    if ( addrsize == 2 )
        addr = *( ushort * )( cmd + 1 );
    else
    {
        addr = *( ulong * )( cmd + 1 );

        if ( pfixup == NULL ) pfixup = cmd + 1;

        da->fixupsize += 4;
    };

    da->adrconst = addr;

    if ( addr == 0 ) da->zeroconst = 1;

    if ( mode >= DISASM_FILE )
    {
        Memadr( SEG_DS, "", addr, datasize );
    };
};

// Decodes jump relative to nextip of size offsize.
static void DecodeRJ( ulong offsize, ulong nextip )
{
    int i;
    ulong addr;
    char s[TEXTLEN];

    if ( size < offsize + 1 )
    {
        da->error = DAE_CROSS;
        return;
    };

    dispsize = offsize;                  // Interpret offset as displacement

    if ( mode < DISASM_DATA ) return;

    if ( offsize == 1 )
        addr = ( signed char )cmd[1] + nextip;
    else if ( offsize == 2 )
        addr = *( signed short * )( cmd + 1 ) + nextip;
    else
        addr = *( ulong * )( cmd + 1 ) + nextip;

    if ( datasize == 2 )
        addr &= 0xFFFF;

    da->jmpconst = addr;

    if ( addr == 0 ) da->zeroconst = 1;

    if ( mode >= DISASM_FILE )
    {
        if ( offsize == 1 ) nresult += sprintf( da->result + nresult,
                                                    "%s ", ( lowercase == 0 ? "SHORT" : "short" ) );

        if ( mode >= DISASM_CODE )
            i = Decodeaddress( addr, s, TEXTLEN, da->comment );
        else
            i = 0;

        if ( symbolic == 0 || i == 0 )
            nresult += sprintf( da->result + nresult, "%08lX", addr );
        else
            nresult += sprintf( da->result + nresult, "%.*s", TEXTLEN - nresult - 25, s );

        if ( symbolic == 0 && i != 0 && da->comment[0] == '\0' )
            strcpy( da->comment, s );

        ;
    };
};

// Decode immediate absolute far jump address. In flat model, such addresses
// are not used (mostly because selector is specified directly in the command),
// so I neither decode as symbol nor comment it. To allow search for selector
// by value, I interprete it as an immediate constant.
static void DecodeJF( void )
{
    ulong addr, seg;

    if ( size < 1 + addrsize + 2 )
    {
        da->error = DAE_CROSS;
        return;
    };

    dispsize = addrsize;

    immsize = 2;      // Non-trivial but allowed interpretation

    if ( mode < DISASM_DATA ) return;

    if ( addrsize == 2 )
    {
        addr = *( ushort * )( cmd + 1 );
        seg = *( ushort * )( cmd + 3 );
    }
    else
    {
        addr = *( ulong * )( cmd + 1 );
        seg = *( ushort * )( cmd + 5 );
    };

    da->jmpconst = addr;

    da->immconst = seg;

    if ( addr == 0 || seg == 0 ) da->zeroconst = 1;

    if ( mode >= DISASM_FILE )
    {
        nresult += sprintf( da->result + nresult, "%s %04X:%08X",
                            ( lowercase == 0 ? "FAR" : "far" ), seg, addr );
    };
};

// Decode segment register. In flat model, operands of this type are seldom.
static void DecodeSG( int index )
{
    int i;

    if ( mode < DISASM_DATA ) return;

    index &= 0x07;

    if ( index >= 6 ) softerror = DAE_BADSEG; // Undefined segment register

    if ( mode >= DISASM_FILE )
    {
        i = sprintf( da->result + nresult, "%s", segname[index] );

        if ( lowercase ) strlwr( da->result + nresult );

        nresult += i;
    };
};

// Decode control register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. Contents of control registers are accessible
// only from privilege level 0, so I cannot dump them here.
static void DecodeCR( int index )
{
    hasrm = 1;

    if ( mode >= DISASM_FILE )
    {
        index = ( index >> 3 ) & 0x07;
        nresult += sprintf( da->result + nresult, "%s", crname[index] );

        if ( lowercase ) strlwr( da->result + nresult );
    };
};

// Decode debug register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. I can dump only those debug registers
// available in CONTEXT structure.
static void DecodeDR( int index )
{
    int i;
    hasrm = 1;

    if ( mode >= DISASM_FILE )
    {
        index = ( index >> 3 ) & 0x07;
        i = sprintf( da->result + nresult, "%s", drname[index] );

        if ( lowercase ) strlwr( da->result + nresult );

        nresult += i;
    };
};

// Skips 3DNow! operands and extracts command suffix. Returns suffix or -1 if
// suffix lies outside the memory block. This subroutine assumes that cmd still
// points to the beginning of 3DNow! command (i.e. to the sequence of two bytes
// 0F, 0F).
static int Get3dnowsuffix( void )
{
    int c, sib;
    ulong offset;

    if ( size < 3 ) return -1;           // Suffix outside the memory block

    offset = 3;
    c = cmd[2] & 0xC7;                   // Leave only Mod and M fields

    // Register in ModM - general-purpose, MMX or 3DNow!
    if ( ( c & 0xC0 ) == 0xC0 )
        ;
    // 16-bit addressing mode, SIB byte is never used here.
    else if ( addrsize == 2 )
    {
        if ( c == 0x06 )                   // Special case of immediate address
            offset += 2;
        else if ( ( c & 0xC0 ) == 0x40 )   // 8-bit signed displacement
            offset++;
        else if ( ( c & 0xC0 ) == 0x80 )   // 16-bit unsigned displacement
            offset += 2;

        ;
    }
    // Immediate 32-bit address.
    else if ( c == 0x05 )                // Special case of immediate address
        offset += 4;
    // 32-bit address with SIB byte.
    else if ( ( c & 0x07 ) == 0x04 )     // SIB addresation
    {
        if ( size < 4 ) return -1;         // Suffix outside the memory block

        sib = cmd[3];
        offset++;

        if ( c == 0x04 && ( sib & 0x07 ) == 0x05 )
            offset += 4;                     // Immediate address without base
        else if ( ( c & 0xC0 ) == 0x40 )   // 8-bit displacement
            offset += 1;
        else if ( ( c & 0xC0 ) == 0x80 )   // 32-bit dislacement
            offset += 4;

        ;
    }
    // 32-bit address without SIB byte
    else if ( ( c & 0xC0 ) == 0x40 )
        offset += 1;
    else if ( ( c & 0xC0 ) == 0x80 )
        offset += 4;

    if ( offset >= size ) return -1;     // Suffix outside the memory block

    return cmd[offset];
};

// Function checks whether 80x86 flags meet condition set in the command.
// Returns 1 if condition is met, 0 if not and -1 in case of error (which is
// not possible).
int Checkcondition( int code, ulong flags )
{
    ulong cond, temp;

    switch ( code & 0x0E )
    {
    case 0:                            // If overflow
        cond = flags & 0x0800;
        break;

    case 2:                            // If below
        cond = flags & 0x0001;
        break;

    case 4:                            // If equal
        cond = flags & 0x0040;
        break;

    case 6:                            // If below or equal
        cond = flags & 0x0041;
        break;

    case 8:                            // If sign
        cond = flags & 0x0080;
        break;

    case 10:                           // If parity
        cond = flags & 0x0004;
        break;

    case 12:                           // If less
        temp = flags & 0x0880;
        cond = ( temp == 0x0800 || temp == 0x0080 );
        break;

    case 14:                           // If less or equal
        temp = flags & 0x0880;
        cond = ( temp == 0x0800 || temp == 0x0080 || ( flags & 0x0040 ) != 0 );
        break;

    default:
        return -1;                // Internal error, not possible!
    };

    if ( ( code & 0x01 ) == 0 ) return ( cond != 0 );
    else return ( cond == 0 );           // Invert condition
};

ulong Disasm( char *src, ulong srcsize, ulong srcip,
              t_disasm *disasm, int disasmmode )
{
    int i, j, isprefix, is3dnow, repeated, operand, mnemosize, arg;
    ulong u, code;
    int lockprefix;                      // Non-zero if lock prefix present
    int repprefix;                       // REPxxx prefix or 0
    int cxsize;
    char name[TEXTLEN], *pname;
    const t_cmddata *pd, *pdan;
    // Prepare disassembler variables and initialize structure disasm.
    datasize = addrsize = 4;             // 32-bit code and data segments only!
    segprefix = SEG_UNDEF;
    hasrm = hassib = 0;
    dispsize = immsize = 0;
    lockprefix = 0;
    repprefix = 0;
    ndump = 0;
    nresult = 0;
    cmd = src;
    size = srcsize;
    pfixup = NULL;
    softerror = 0;
    is3dnow = 0;
    da = disasm;
    da->ip = srcip;
    da->comment[0] = '\0';
    da->cmdtype = C_BAD;
    da->nprefix = 0;
    da->memtype = DEC_UNKNOWN;
    da->indexed = 0;
    da->jmpconst = 0;
    da->jmptable = 0;
    da->adrconst = 0;
    da->immconst = 0;
    da->zeroconst = 0;
    da->fixupoffset = 0;
    da->fixupsize = 0;
    da->warnings = 0;
    da->error = DAE_NOERR;
    mode = disasmmode;                   // No need to use register contents
    // Correct 80x86 command may theoretically contain up to 4 prefixes belonging
    // to different prefix groups. This limits maximal possible size of the
    // command to MAXCMDSIZE=16 bytes. In order to maintain this limit, if
    // Disasm() detects second prefix from the same group, it flushes first
    // prefix in the sequence as a pseudocommand.
    u = 0;
    repeated = 0;

    while ( size > 0 )
    {
        isprefix = 1;                      // Assume that there is some prefix

        switch ( *cmd )
        {
        case 0x26:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_ES;
            else repeated = 1;

            break;

        case 0x2E:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_CS;
            else repeated = 1;

            break;

        case 0x36:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_SS;
            else repeated = 1;

            break;

        case 0x3E:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_DS;
            else repeated = 1;

            break;

        case 0x64:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_FS;
            else repeated = 1;

            break;

        case 0x65:
            if ( segprefix == SEG_UNDEF ) segprefix = SEG_GS;
            else repeated = 1;

            break;

        case 0x66:
            if ( datasize == 4 ) datasize = 2;
            else repeated = 1;

            break;

        case 0x67:
            if ( addrsize == 4 ) addrsize = 2;
            else repeated = 1;

            break;

        case 0xF0:
            if ( lockprefix == 0 ) lockprefix = 0xF0;
            else repeated = 1;

            break;

        case 0xF2:
            if ( repprefix == 0 ) repprefix = 0xF2;
            else repeated = 1;

            break;

        case 0xF3:
            if ( repprefix == 0 ) repprefix = 0xF3;
            else repeated = 1;

            break;

        default:
            isprefix = 0;
            break;
        };

        if ( isprefix == 0 || repeated != 0 )
            break;                           // No more prefixes or duplicated prefix

        if ( mode >= DISASM_FILE )
            ndump += sprintf( da->dump + ndump, "%02X:", *cmd );

        da->nprefix++;
        cmd++;
        srcip++;
        size--;
        u++;
    };

    // We do have repeated prefix. Flush first prefix from the sequence.
    if ( repeated )
    {
        if ( mode >= DISASM_FILE )
        {
            da->dump[3] = '\0';              // Leave only first dumped prefix
            da->nprefix = 1;

            switch ( cmd[-( long )u] )
            {
            case 0x26:
                pname = ( char * )( segname[SEG_ES] );
                break;

            case 0x2E:
                pname = ( char * )( segname[SEG_CS] );
                break;

            case 0x36:
                pname = ( char * )( segname[SEG_SS] );
                break;

            case 0x3E:
                pname = ( char * )( segname[SEG_DS] );
                break;

            case 0x64:
                pname = ( char * )( segname[SEG_FS] );
                break;

            case 0x65:
                pname = ( char * )( segname[SEG_GS] );
                break;

            case 0x66:
                pname = "DATASIZE";
                break;

            case 0x67:
                pname = "ADDRSIZE";
                break;

            case 0xF0:
                pname = "LOCK";
                break;

            case 0xF2:
                pname = "REPNE";
                break;

            case 0xF3:
                pname = "REPE";
                break;

            default:
                pname = "?";
                break;
            };

            nresult += sprintf( da->result + nresult, "PREFIX %s:", pname );

            if ( lowercase ) strlwr( da->result );

            if ( extraprefix == 0 ) strcpy( da->comment, "Superfluous prefix" );
        };

        da->warnings |= DAW_PREFIX;

        if ( lockprefix ) da->warnings |= DAW_LOCK;

        da->cmdtype = C_RARE;
        return 1;                          // Any prefix is 1 byte long
    };

    // If lock prefix available, display it and forget, because it has no
    // influence on decoding of rest of the command.
    if ( lockprefix != 0 )
    {
        if ( mode >= DISASM_FILE ) nresult += sprintf( da->result + nresult, "LOCK " );

        da->warnings |= DAW_LOCK;
    };

    // Fetch (if available) first 3 bytes of the command, add repeat prefix and
    // find command in the command table.
    code = 0;

    if ( size > 0 ) *( ( ( char * )&code ) + 0 ) = cmd[0];

    if ( size > 1 ) *( ( ( char * )&code ) + 1 ) = cmd[1];

    if ( size > 2 ) *( ( ( char * )&code ) + 2 ) = cmd[2];

    if ( repprefix != 0 )                // RER/REPE/REPNE is considered to be
        code = ( code << 8 ) | repprefix;  // part of command.

    if ( decodevxd && ( code & 0xFFFF ) == 0x20CD )
        pd = &vxdcmd;                      // Decode VxD call (Win95/98)
    else
    {
        for ( pd = cmddata; pd->mask != 0; pd++ )
        {
            if ( ( ( code ^ pd->code ) & pd->mask ) != 0 ) continue;

            if ( mode >= DISASM_FILE && shortstringcmds &&
                    ( pd->arg1 == MSO || pd->arg1 == MDE || pd->arg2 == MSO || pd->arg2 == MDE ) )
                continue;                      // Search short form of string command

            break;
        };
    };

    if ( ( pd->type & C_TYPEMASK ) == C_NOW )
    {
        // 3DNow! commands require additional search.
        is3dnow = 1;
        j = Get3dnowsuffix();

        if ( j < 0 )
            da->error = DAE_CROSS;
        else
        {
            for ( ; pd->mask != 0; pd++ )
            {
                if ( ( ( code ^ pd->code ) & pd->mask ) != 0 ) continue;

                if ( ( ( uchar * ) & ( pd->code ) )[2] == j ) break;
            };
        };
    };

    if ( pd->mask == 0 )                 // Command not found
    {
        da->cmdtype = C_BAD;

        if ( size < 2 ) da->error = DAE_CROSS;
        else da->error = DAE_BADCMD;
    }
    else                                 // Command recognized, decode it
    {
        da->cmdtype = pd->type;
        cxsize = datasize;                 // Default size of ECX used as counter

        if ( segprefix == SEG_FS || segprefix == SEG_GS || lockprefix != 0 )
            da->cmdtype |= C_RARE;           // These prefixes are rare

        if ( pd->bits == PR )
            da->warnings |= DAW_PRIV;        // Privileged command (ring 0)
        else if ( pd->bits == WP )
            da->warnings |= DAW_IO;          // I/O command

        // Win32 programs usually try to keep stack dword-aligned, so INC ESP
        // (44) and DEC ESP (4C) usually don't appear in real code. Also check for
        // ADD ESP,imm and SUB ESP,imm (81,C4,imm32; 83,C4,imm8; 81,EC,imm32;
        // 83,EC,imm8).
        if ( cmd[0] == 0x44 || cmd[0] == 0x4C ||
                ( size >= 3 && ( cmd[0] == 0x81 || cmd[0] == 0x83 ) &&
                  ( cmd[1] == 0xC4 || cmd[1] == 0xEC ) && ( cmd[2] & 0x03 ) != 0 )
           )
        {
            da->warnings |= DAW_STACK;
            da->cmdtype |= C_RARE;
        };

        // Warn also on MOV SEG,... (8E...). Win32 works in flat mode.
        if ( cmd[0] == 0x8E )
            da->warnings |= DAW_SEGMENT;

        // If opcode is 2-byte, adjust command.
        if ( pd->len == 2 )
        {
            if ( size == 0 ) da->error = DAE_CROSS;
            else
            {
                if ( mode >= DISASM_FILE )
                    ndump += sprintf( da->dump + ndump, "%02X", *cmd );

                cmd++;
                srcip++;
                size--;
            };
        };

        if ( size == 0 ) da->error = DAE_CROSS;

        // Some commands either feature non-standard data size or have bit which
        // allowes to select data size.
        if ( ( pd->bits & WW ) != 0 && ( *cmd & WW ) == 0 )
            datasize = 1;                    // Bit W in command set to 0
        else if ( ( pd->bits & W3 ) != 0 && ( *cmd & W3 ) == 0 )
            datasize = 1;                    // Another position of bit W
        else if ( ( pd->bits & FF ) != 0 )
            datasize = 2;                    // Forced word (2-byte) size

        // Some commands either have mnemonics which depend on data size (8/16 bits
        // or 32 bits, like CWD/CDQ), or have several different mnemonics (like
        // JNZ/JNE). First case is marked by either '&' (mnemonic depends on
        // operand size) or '$' (depends on address size). In the second case,
        // there is no special marker and disassembler selects main mnemonic.
        if ( mode >= DISASM_FILE )
        {
            if ( pd->name[0] == '&' ) mnemosize = datasize;
            else if ( pd->name[0] == '$' ) mnemosize = addrsize;
            else mnemosize = 0;

            if ( mnemosize != 0 )
            {
                for ( i = 0, j = 1; pd->name[j] != '\0'; j++ )
                {
                    if ( pd->name[j] == ':' )    // Separator between 16/32 mnemonics
                    {
                        if ( mnemosize == 4 ) i = 0;
                        else break;
                    }
                    else if ( pd->name[j] == '*' ) // Substitute by 'W', 'D' or none
                    {
                        if ( mnemosize == 4 && sizesens != 2 ) name[i++] = 'D';
                        else if ( mnemosize != 4 && sizesens != 0 ) name[i++] = 'W';
                    }
                    else name[i++] = pd->name[j];
                };

                name[i] = '\0';
            }
            else
            {
                strcpy( name, pd->name );

                for ( i = 0; name[i] != '\0'; i++ )
                {
                    if ( name[i] == ',' )        // Use main mnemonic
                    {
                        name[i] = '\0';
                        break;
                    };
                };
            };

            if ( repprefix != 0 && tabarguments )
            {
                for ( i = 0; name[i] != '\0' && name[i] != ' '; i++ )
                    da->result[nresult++] = name[i];

                if ( name[i] == ' ' )
                {
                    da->result[nresult++] = ' ';
                    i++;
                };

                while ( nresult < 8 ) da->result[nresult++] = ' ';

                for ( ; name[i] != '\0'; i++ )
                    da->result[nresult++] = name[i];

                ;
            }
            else
                nresult += sprintf( da->result + nresult, "%s", name );

            if ( lowercase ) strlwr( da->result );
        };

        // Decode operands (explicit - encoded in command, implicit - present in
        // mmemonic or assumed - used or modified by command). Assumed operands
        // must stay after all explicit and implicit operands. Up to 3 operands
        // are allowed.
        for ( operand = 0; operand < 3; operand++ )
        {
            if ( da->error ) break;          // Error - no sense to continue

            // If command contains both source and destination, one usually must not
            // decode destination to comment because it will be overwritten on the
            // next step. Global addcomment takes care of this. Decoding routines,
            // however, may ignore this flag.
            if ( operand == 0 && pd->arg2 != NNN && pd->arg2 < PSEUDOOP )
                addcomment = 0;
            else
                addcomment = 1;

            // Get type of next argument.
            if ( operand == 0 ) arg = pd->arg1;
            else if ( operand == 1 ) arg = pd->arg2;
            else arg = pd->arg3;

            if ( arg == NNN ) break;         // No more operands

            // Arguments with arg>=PSEUDOOP are assumed operands and are not
            // displayed in disassembled result, so they require no delimiter.
            if ( ( mode >= DISASM_FILE ) && arg < PSEUDOOP )
            {
                if ( operand == 0 )
                {
                    da->result[nresult++] = ' ';

                    if ( tabarguments )
                    {
                        while ( nresult < 8 ) da->result[nresult++] = ' ';
                    };
                }
                else
                {
                    da->result[nresult++] = ',';

                    if ( extraspace ) da->result[nresult++] = ' ';
                };
            };

            // Decode, analyse and comment next operand of the command.
            switch ( arg )
            {
            case REG:                      // Integer register in Reg field
                if ( size < 2 ) da->error = DAE_CROSS;
                else DecodeRG( cmd[1] >> 3, datasize, REG );

                hasrm = 1;
                break;

            case RCM:                      // Integer register in command byte
                DecodeRG( cmd[0], datasize, RCM );
                break;

            case RG4:                      // Integer 4-byte register in Reg field
                if ( size < 2 ) da->error = DAE_CROSS;
                else DecodeRG( cmd[1] >> 3, 4, RG4 );

                hasrm = 1;
                break;

            case RAC:                      // Accumulator (AL/AX/EAX, implicit)
                DecodeRG( REG_EAX, datasize, RAC );
                break;

            case RAX:                      // AX (2-byte, implicit)
                DecodeRG( REG_EAX, 2, RAX );
                break;

            case RDX:                      // DX (16-bit implicit port address)
                DecodeRG( REG_EDX, 2, RDX );
                break;

            case RCL:                      // Implicit CL register (for shifts)
                DecodeRG( REG_ECX, 1, RCL );
                break;

            case RS0:                      // Top of FPU stack (ST(0))
                DecodeST( 0, 0 );
                break;

            case RST:                      // FPU register (ST(i)) in command byte
                DecodeST( cmd[0], 0 );
                break;

            case RMX:                      // MMX register MMx
                if ( size < 2 ) da->error = DAE_CROSS;
                else DecodeMX( cmd[1] >> 3 );

                hasrm = 1;
                break;

            case R3D:                      // 3DNow! register MMx
                if ( size < 2 ) da->error = DAE_CROSS;
                else DecodeNR( cmd[1] >> 3 );

                hasrm = 1;
                break;

            case MRG:                      // Memory/register in ModRM byte
            case MRJ:                      // Memory/reg in ModRM as JUMP target
            case MR1:                      // 1-byte memory/register in ModRM byte
            case MR2:                      // 2-byte memory/register in ModRM byte
            case MR4:                      // 4-byte memory/register in ModRM byte
            case MR8:                      // 8-byte memory/MMX register in ModRM
            case MRD:                      // 8-byte memory/3DNow! register in ModRM
            case MMA:                      // Memory address in ModRM byte for LEA
            case MML:                      // Memory in ModRM byte (for LES)
            case MM6:                      // Memory in ModRm (6-byte descriptor)
            case MMB:                      // Two adjacent memory locations (BOUND)
            case MD2:                      // Memory in ModRM byte (16-bit integer)
            case MB2:                      // Memory in ModRM byte (16-bit binary)
            case MD4:                      // Memory in ModRM byte (32-bit integer)
            case MD8:                      // Memory in ModRM byte (64-bit integer)
            case MDA:                      // Memory in ModRM byte (80-bit BCD)
            case MF4:                      // Memory in ModRM byte (32-bit float)
            case MF8:                      // Memory in ModRM byte (64-bit float)
            case MFA:                      // Memory in ModRM byte (80-bit float)
            case MFE:                      // Memory in ModRM byte (FPU environment)
            case MFS:                      // Memory in ModRM byte (FPU state)
            case MFX:                      // Memory in ModRM byte (ext. FPU state)
                DecodeMR( arg );
                break;

            case MMS:                      // Memory in ModRM byte (as SEG:OFFS)
                DecodeMR( arg );
                da->warnings |= DAW_FARADDR;
                break;

            case RR4:                      // 4-byte memory/register (register only)
            case RR8:                      // 8-byte MMX register only in ModRM
            case RRD:                      // 8-byte memory/3DNow! (register only)
                if ( ( cmd[1] & 0xC0 ) != 0xC0 ) softerror = DAE_REGISTER;

                DecodeMR( arg );
                break;

            case MSO:                      // Source in string op's ([ESI])
                DecodeSO();
                break;

            case MDE:                      // Destination in string op's ([EDI])
                DecodeDE();
                break;

            case MXL:                      // XLAT operand ([EBX+AL])
                DecodeXL();
                break;

            case IMM:                      // Immediate data (8 or 16/32)
            case IMU:                      // Immediate unsigned data (8 or 16/32)
                if ( ( pd->bits & SS ) != 0 && ( *cmd & 0x02 ) != 0 )
                    DecodeIM( 1, datasize, arg );
                else
                    DecodeIM( datasize, 0, arg );

                break;

            case VXD:                      // VxD service (32-bit only)
                DecodeVX();
                break;

            case IMX:                      // Immediate sign-extendable byte
                DecodeIM( 1, datasize, arg );
                break;

            case C01:                      // Implicit constant 1 (for shifts)
                DecodeC1();
                break;

            case IMS:                      // Immediate byte (for shifts)
            case IM1:                      // Immediate byte
                DecodeIM( 1, 0, arg );
                break;

            case IM2:                      // Immediate word (ENTER/RET)
                DecodeIM( 2, 0, arg );

                if ( ( da->immconst & 0x03 ) != 0 ) da->warnings |= DAW_STACK;

                break;

            case IMA:                      // Immediate absolute near data address
                DecodeIA();
                break;

            case JOB:                      // Immediate byte offset (for jumps)
                DecodeRJ( 1, srcip + 2 );
                break;

            case JOW:                      // Immediate full offset (for jumps)
                DecodeRJ( datasize, srcip + datasize + 1 );
                break;

            case JMF:                      // Immediate absolute far jump/call addr
                DecodeJF();
                da->warnings |= DAW_FARADDR;
                break;

            case SGM:                      // Segment register in ModRM byte
                if ( size < 2 ) da->error = DAE_CROSS;

                DecodeSG( cmd[1] >> 3 );
                hasrm = 1;
                break;

            case SCM:                      // Segment register in command byte
                DecodeSG( cmd[0] >> 3 );

                if ( ( da->cmdtype & C_TYPEMASK ) == C_POP ) da->warnings |= DAW_SEGMENT;

                break;

            case CRX:                      // Control register CRx
                if ( ( cmd[1] & 0xC0 ) != 0xC0 ) da->error = DAE_REGISTER;

                DecodeCR( cmd[1] );
                break;

            case DRX:                      // Debug register DRx
                if ( ( cmd[1] & 0xC0 ) != 0xC0 ) da->error = DAE_REGISTER;

                DecodeDR( cmd[1] );
                break;

            case PRN:                      // Near return address (pseudooperand)
                break;

            case PRF:                      // Far return address (pseudooperand)
                da->warnings |= DAW_FARADDR;
                break;

            case PAC:                      // Accumulator (AL/AX/EAX, pseudooperand)
                DecodeRG( REG_EAX, datasize, PAC );
                break;

            case PAH:                      // AH (in LAHF/SAHF, pseudooperand)
            case PFL:                      // Lower byte of flags (pseudooperand)
                break;

            case PS0:                      // Top of FPU stack (pseudooperand)
                DecodeST( 0, 1 );
                break;

            case PS1:                      // ST(1) (pseudooperand)
                DecodeST( 1, 1 );
                break;

            case PCX:                      // CX/ECX (pseudooperand)
                DecodeRG( REG_ECX, cxsize, PCX );
                break;

            case PDI:                      // EDI (pseudooperand in MMX extentions)
                DecodeRG( REG_EDI, 4, PDI );
                break;

            default:
                da->error = DAE_INTERN;      // Unknown argument type
                break;
            };
        };

        // Check whether command may possibly contain fixups.
        if ( pfixup != NULL && da->fixupsize > 0 )
            da->fixupoffset = pfixup - src;

        // Segment prefix and address size prefix are superfluous for command which
        // does not access memory. If this the case, mark command as rare to help
        // in analysis.
        if ( da->memtype == DEC_UNKNOWN &&
                ( segprefix != SEG_UNDEF || ( addrsize != 4 && pd->name[0] != '$' ) )
           )
        {
            da->warnings |= DAW_PREFIX;
            da->cmdtype |= C_RARE;
        };

        // 16-bit addressing is rare in 32-bit programs. If this is the case,
        // mark command as rare to help in analysis.
        if ( addrsize != 4 ) da->cmdtype |= C_RARE;
    };

    // Suffix of 3DNow! command is accounted best by assuming it immediate byte
    // constant.
    if ( is3dnow )
    {
        if ( immsize != 0 ) da->error = DAE_BADCMD;
        else immsize = 1;
    };

    // Right or wrong, command decoded. Now dump it.
    if ( da->error != 0 )                // Hard error in command detected
    {
        if ( mode >= DISASM_FILE )
            nresult = sprintf( da->result, "???" );

        if ( da->error == DAE_BADCMD &&
                ( *cmd == 0x0F || *cmd == 0xFF ) && size > 0
           )
        {
            if ( mode >= DISASM_FILE ) ndump += sprintf( da->dump + ndump, "%02X", *cmd );

            cmd++;
            size--;
        };

        if ( size > 0 )
        {
            if ( mode >= DISASM_FILE ) ndump += sprintf( da->dump + ndump, "%02X", *cmd );

            cmd++;
            size--;
        };
    }
    else                                 // No hard error, dump command
    {
        if ( mode >= DISASM_FILE )
        {
            ndump += sprintf( da->dump + ndump, "%02X", *cmd++ );

            if ( hasrm ) ndump += sprintf( da->dump + ndump, "%02X", *cmd++ );

            if ( hassib ) ndump += sprintf( da->dump + ndump, "%02X", *cmd++ );

            if ( dispsize != 0 )
            {
                da->dump[ndump++] = ' ';

                for ( i = 0; i < dispsize; i++ )
                {
                    ndump += sprintf( da->dump + ndump, "%02X", *cmd++ );
                };
            };

            if ( immsize != 0 )
            {
                da->dump[ndump++] = ' ';

                for ( i = 0; i < immsize; i++ )
                {
                    ndump += sprintf( da->dump + ndump, "%02X", *cmd++ );
                };
            };
        }
        else
            cmd += 1 + hasrm + hassib + dispsize + immsize;

        size -= 1 + hasrm + hassib + dispsize + immsize;
    };

    // Check that command is not a dangerous one.
    if ( mode >= DISASM_DATA )
    {
        for ( pdan = dangerous; pdan->mask != 0; pdan++ )
        {
            if ( ( ( code ^ pdan->code ) & pdan->mask ) != 0 )
                continue;

            if ( pdan->type == C_DANGERLOCK && lockprefix == 0 )
                break;                         // Command harmless without LOCK prefix

            if ( iswindowsnt && pdan->type == C_DANGER95 )
                break;                         // Command harmless under Windows NT

            // Dangerous command!
            if ( pdan->type == C_DANGER95 ) da->warnings |= DAW_DANGER95;
            else da->warnings |= DAW_DANGEROUS;

            break;
        };
    };

    if ( da->error == 0 && softerror != 0 )
        da->error = softerror;             // Error, but still display command

    if ( mode >= DISASM_FILE )
    {
        if ( da->error != DAE_NOERR ) switch ( da->error )
            {
            case DAE_CROSS:
                strcpy( da->comment, "Command crosses end of memory block" );
                break;

            case DAE_BADCMD:
                strcpy( da->comment, "Unknown command" );
                break;

            case DAE_BADSEG:
                strcpy( da->comment, "Undefined segment register" );
                break;

            case DAE_MEMORY:
                strcpy( da->comment, "Illegal use of register" );
                break;

            case DAE_REGISTER:
                strcpy( da->comment, "Memory address not allowed" );
                break;

            case DAE_INTERN:
                strcpy( da->comment, "Internal OLLYDBG error" );
                break;

            default:
                strcpy( da->comment, "Unknown error" );
                break;
            }
        else if ( ( da->warnings & DAW_PRIV ) != 0 && privileged == 0 )
            strcpy( da->comment, "Privileged command" );
        else if ( ( da->warnings & DAW_IO ) != 0 && iocommand == 0 )
            strcpy( da->comment, "I/O command" );
        else if ( ( da->warnings & DAW_FARADDR ) != 0 && farcalls == 0 )
        {
            if ( ( da->cmdtype & C_TYPEMASK ) == C_JMP )
                strcpy( da->comment, "Far jump" );
            else if ( ( da->cmdtype & C_TYPEMASK ) == C_CAL )
                strcpy( da->comment, "Far call" );
            else if ( ( da->cmdtype & C_TYPEMASK ) == C_RET )
                strcpy( da->comment, "Far return" );

            ;
        }
        else if ( ( da->warnings & DAW_SEGMENT ) != 0 && farcalls == 0 )
            strcpy( da->comment, "Modification of segment register" );
        else if ( ( da->warnings & DAW_SHIFT ) != 0 && badshift == 0 )
            strcpy( da->comment, "Shift constant out of range 1..31" );
        else if ( ( da->warnings & DAW_PREFIX ) != 0 && extraprefix == 0 )
            strcpy( da->comment, "Superfluous prefix" );
        else if ( ( da->warnings & DAW_LOCK ) != 0 && lockedbus == 0 )
            strcpy( da->comment, "LOCK prefix" );
        else if ( ( da->warnings & DAW_STACK ) != 0 && stackalign == 0 )
            strcpy( da->comment, "Unaligned stack operation" );

        ;
    };

    return ( srcsize - size );           // Returns number of recognized bytes
};

